26
תגובות
לדוגמה אם אני בונה מערכת של משתמשים עם תגובות וכו',
אז אני מתאר שזה יהיה מאוד מסורבל ולא כדאי לעשות מחלקה אחת ממש גדולה
שתכיל בתוכה את כל המתודות לחיבור למסד נתונים, בדיקת התחברות משתמשים,
התנתקות, הוספת תגובה חדשה וכו', אלא לעשות את זה בקלאסים נפרדים.
אבל הבעיה היא שאם אני יוצר יותר מקלאס אחד לחיבור עם מסד נתונים אז זה לא נראה
לי ממש יעיל שיהיו לי 2 חיבורים לאותו מסד כשאני יכול להשתמש באחד..

לדוגמה:

class user extends mysqli
{
  function __construct($host , $user , $pass , $db)
  {
    parent::__construct($host,$user,$pass,$db);
  }
 
  function login()
  {
  // log in code..
  }
 
  function logout()
  {
  // log out code..
  }
}

class forum extends mysqli
{
  function __construct($host , $user , $pass , $db)
  {
    parent::__construct($host,$user,$pass,$db);
  }
 
  function addComment()
  {
  // add comment code..
  }
 
}


פה אני צריך להשתמש ב-2 חיבורים שונים לאותו מסד נתונים בשביל המחלקות השונות.

מה האפשרות הכי טובה במצב כזה? ;-)

26 תשובות

avatar ענה cthulhu ב 29 ליוני 2012 #

למה שתוריש מ-mysqli? איך האובייקטים הללו קשורים ל-mysqli מלבד העובדה שיש לך צורך להשתמש בפעולות של mysqli? יש הבדל בין הורשה לבין include/require.

וגם כשתצמצם הכול לחיבור אחד, לא הייתי ממליץ לך לעשות סינגלטון כי זה לא באמת מועיל מאוד.

avatar ענה Plural ב 30 ליוני 2012 #

אה הבנתי למה אתה מתכוון, באמת אין צורך.

ורק כדי שאני אהיה בטוח, זה הדבר הנכון לעשות?:

class user
{
  static $con;
 
  function __construct($host , $user , $pass , $db)
  {
    self::$con = new mysqli($host,$user,$pass,$db);
  }
 
  function login()
  {
  $query = self::$con->query("SELECT...");
  }
 
  function logout()
  {
  // log out code..
  }
}

$db = new user('localhost' , 'root' , '' , 'database');
$db->login(); // סתם לדוגמה


ובקשר לשאלה שלי, עדיין לא לגמרי הבנתי מה כדאי לעשות. אשמח אם תסביר שוב. (-:

avatar ענה iiddaannyy ב 30 ליוני 2012 #

לא.
למה user צריכה לקבל פרטי חיבור למסד?

class user {
    private $id;
    public $username, $age;
    public function __construct($id) {
        $this->id = $id;
    }
    public function doSomething() {
        $mysqli = new mysqli(....);
        $q = $mysqli->query("query...");
    }
}

אצלי פרטי הכניסה שמורים כמאפיינים במחלקה עצמה של mysqli, ככה שאני לא צריך להזין אותם בכל חיבור.
*שים לב שבכל מתודה שמתקשרת עם המסד, תצטרך להתחבר מחדש. אני משתמש ב-singleton כדי למנוע המון חיבורים.

avatar ענה Plural ב 30 ליוני 2012 #

"*שים לב שבכל מתודה שמתקשרת עם המסד, תצטרך להתחבר מחדש. אני משתמש ב-singleton כדי למנוע המון חיבורים."

למה? לא הבנתי מה הבעיה ליצור תכונה של החיבור למסד במחלקה (כמו שעשיתי - con$), ואז בכל מתודה להשתמש בחיבור הקיים?
ובקשר לסינגלטון - אני אבדוק את זה לעומק. תודה על התגובה. (:

avatar ענה Plural ב 30 ליוני 2012 #

ד"א, אם אני יוצר מופע mysqli אחד, ואז מעביר אותו (או מצביע אליו) לכל קלאס כפרמטר (אפשר גם להגיע אליו כמשתנה גלובלי) ואז להשתמש בו, זה יהיה יעיל?

avatar ענה iiddaannyy ב 30 ליוני 2012 #

אין בעיה ליצור תכונה סטטית של החיבור למסד. אבל זה לא אמור להיות שם.
מחלקה של משתמש אמורה להכיל רק מה שקשור למשתמש. החיבור לא קשור אליו.

ואם אתה כבר יוצר מופע mysqli אחד, פשוט תשתמש ב-singleton. ככה אתה לא תצטרך לשלוח אותו כל פעם כפרמטר.
בכל מקום תוכל לקבל את החיבור ולעשות איתו מה שתרצה.

avatar ענה cthulhu ב 30 ליוני 2012 #

סינגלטון מגביל אותך בכמה חיבורים, וזה לא דבר חכם, כי לפעמים יש צורך להתחבר למסדים שונים. תצור מופע אחד ותשתמש בו.

avatar ענה Plural ב 30 ליוני 2012 #

האמת שראיתי באינטרנט הרבה שלא ממליצים על סינגלטון..
אבל מהסיבה הזאת כרגע זה לא בעיה עבורי, כי יש לי רק מסד אחד.

- תצור מופע אחד ותשתמש בו.
איך להשתמש בו? לעשות הכל בקלאס אחד יהיה ממש מסורבל וארוך..\:

avatar ענה iiddaannyy ב 30 ליוני 2012 #

@cthulhu
נכון. אבל אם יש מסד אחד (כמו כאן) אז זה מצוין.
חוץ מזה שאתה תמיד יכול להרחיב את זה ליותר ממסד אחד בכך שתעביר כפרמטר לפונקציה הסטטית (זאת שמחזירה את המופע) את המסד שאליו אתה רוצה את החיבור, והיא תחזיר לך את המופע המתאים. משהו כזה:

class database extends mysqli {
    private static $connection= array();
    public static function connection($db) {
        if (!array_key_exists($db, self::$connection)) {
            self::$connection[$db] = new self('localhost','un','pw',$db):
        }
        return self::$connection[$db];
    }
}
$db1 = database::connection('database1');
$db2 = database::connection('database2');

avatar ענה cthulhu ב 30 ליוני 2012 #

בינתיים יש לו מסד אחד. כשהוא ירצה להוסיף מסד, הוא יצטרך לשנות חיבורים במקומות רבים. להשתמש בסינגלטון בעבודה עם מסדים - לא נכון.

avatar ענה iiddaannyy ב 30 ליוני 2012 #

אם הוא ישתמש במה שהראיתי למעלה הוא יוכל להוסיף כמה מסדים שירצה בלי שום בעיה.

avatar ענה cthulhu ב 30 ליוני 2012 #

זה לא רק עניין של מס' חיבורים למסדים שונים. זה גם עניין של מבנה קוד רע. זה שווה ערך ל-global במבנה. בגלל המבנה שלו אי אפשר לאפשר סאבקלאסים. וכן, object pool זה דבר נחמד, אך עדיין אין כאן צורך בזה.

avatar ענה iiddaannyy ב 30 ליוני 2012 #

אני לא רואה בזה שום בעיה.
המתודה הסטטית פשוט מחזירה לך את החיבור למסד ואתה יכול לגשת אליה ולקבל את החיבור מכל מקום.
ואם בסאבקלאס אתה מתכוון לירושה, אני לא רואה סיבה לרשת מהמעטפת של mysqli.
ובנוגע להדמות שלו ל-global, אולי זה טיפה מזכיר, אבל אל תשכח שב-global אתה לא יודע בוודאות שהמשתנה החיצוני קיים ואתה הופך תלותי בו. כאן זה לא המקרה, אתה מקבל את החיבור, ואם אין חיבור כזה, אז הוא נוצר ואז אתה מקבל אותו.

avatar ענה cthulhu ב 30 ליוני 2012 #

מה הקשר למה שאמרתי? ולגבי "תלותי" - סינגלטון מסתיר תלויות, ככה שעדיף כאן לעשות di.

avatar ענה iiddaannyy ב 30 ליוני 2012 #

סינגלטון הוא לא תלותי, הוא לא מסתיר גם כלום. אתה לא תלוי בחיבור, אתה תמיד תקבל אותו.
למעט במקרה בו יש שגיאה, ובמקרה כזה אין הבדל בין singleton לבין כל דרך אחרת.

avatar ענה Plural ב 30 ליוני 2012 #

הבנתי איך סינגלטון פועל, ואין לי בעיה לממש אותו על אותו הקלאס ש"בונה" אותו,
אבל עדיין לא הבנתי לגמרי איך אני ממש אותו על קלאסים אחרים. (זאת המטרה שפתחתי את הנושא הזה)
אז אני אשמח שתביאו לי קוד לדוגמה של מימוש סינגלטון על יותר מקלאס אחד.

avatar ענה Plural ב 30 ליוני 2012 #

וד"א cthulhu,
הבנתי שאתה לא ממליץ על סינגלטון, אבל על מה אתה כן ממליץ עדיין לא ענית לי..

avatar ענה iiddaannyy ב 30 ליוני 2012 #

אם אתה עושה סינגלטון על המעטפת של mysqli אתה מממש אותו פעם אחת. אתה לא צריך להפוך את כל המחלקות שמשתמשות בו לסינגלטון.

avatar ענה cthulhu ב 30 ליוני 2012 #

אם לדעתך סינגלטון לא מסתיר תלות אז אתה ככל הנראה לא מבין עד הסוף מהו סינגלטון. כל התהליך של יצירת אובייקט מתנהל במתודה סטטית, כך שהשליטה עליו בלתי אפשרית. וכשתרצה לעשות קצת unit testing תופתע לגלות שזה לא יהיה נחמד כ"כ עם סינגלטון ותצטרך להשתמש ב-di כמו שאמרתי קודם לכן.

avatar ענה Plural ב 30 ליוני 2012 #

תראה, אני אחזור על הסיבה שפתחתי את הנושא הזה -
אני רוצה לאפשר כמה קלאסים עם אותו חיבור למסד נתונים.

אז כמו שאמרתי, אני אשמח לקוד קצר שרק יגרום לי להבין את הרעיון לאיך לעשות את זה.

avatar ענה cthulhu ב 30 ליוני 2012 #

תעביר לכל קלאס את המופע היחיד שיש לך בקוד. או שתעשה את זה בקונסטרוקטור או במתודה נפרדת. זה עבודה של 2 שניות. ואתה בכל מקרה תצטרך ליצור את המופע של האובייקטים הללו איכן שהוא בקוד.

avatar ענה iiddaannyy ב 30 ליוני 2012 #

@cthulhu
יכול להיות שאני לא מבין עד הסוף מהו סינגלטון. אשמח אם תסביר מה הבעיות איתו.
אני לא רואה בעיה ביצירת והחזרה של אובייקט במתודה סטטית.

avatar ענה Plural ב 30 ליוני 2012 #

cthulhu,
אתה מתכוון להעביר את המופע באמצעות פרמטר למחלקות?

<?php

class user
{
  private static $msi;
 
  function __construct($con)
  {
    self::$msi = $con;
  }
 
  function show()
  {
    $msi = self::$msi;
    $query = $msi->query("SELECT ...");
  }
}

$con = new mysqli('localhost' , 'root' , '' , 'db');

$msi = new user($con);
$msi->show();


ככה לדוגמה?

וד"א, אני יודע שבשפת C במקרה כמו זה להעביר מצביע כפרמטר יהיה הרבה יותר יעיל וחסכוני ,
מכיוון שמצביע שוקל רק 4 בתים, ביחס למשאב כמו זה שיכול לתפוס יותר מקום בזיכרון.
אז השאלה שלי היא האם גם ב-php זה יהיה אותו דבר? כלומר, שעדיף יהיה לי לעשות את המתודה הקונסטרקטית ככה:

function __construct(&$con)
  {
    self::$msi = $con;
  }


?

avatar ענה cthulhu ב 30 ליוני 2012 #

כן, דרך הקונסטרוקטור. רק בשביל מה לעשות את זה סטטי?

class User {
  private $currentConnection;
 
  public function __construct($dbConnection) {
    $this->currentConnection = $dbConnection;
  }
 
  ...
}

אלא אם כן אתה באמת צריך ליצור מיליון מופעים.

לגבי העברה "by reference" - אין כל צורך להעביר ככה חיבור, כי חיבור ב-php כבר בעצמו אוטומטית מועבר כקישור (reference) ולא כערך. במקרים מסוימים זה אפילו יהיה איטי יותר.

avatar ענה Plural ב 30 ליוני 2012 #

חשבתי שכדאי לעשות את זה סטטי כדי שלא ייוצרו חיבורים שונים למחלקה, אלא אותו חיבור כל הזמן.
ותודה רבה לגבי ההסבר! סוף סוף אני יכול ליישם את מה שרציתי. :)

avatar ענה iiddaannyy ב 30 ליוני 2012 #

לי זה עדיין מוזר להעביר כפרמטר למחלקה משהו שלא קשור למחלקה.

$user = new User($mysqli);

נראה לי מוזר.